﻿using Furion.DatabaseAccessor;
using Furion.LinqBuilder;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using YFurion.Entities;
using YFurion.Extensions;

namespace YFurion.EntityFrameworkCore.MultiTenants
{
    public abstract class MultiTenantMasterContext<TDbContext, TDbContextLocator> : WriteReadDbContext<TDbContext, TDbContextLocator>, IYMultiTenantOnDatabase, IYMultiTenantOnTable
        where TDbContext : DbContext
        where TDbContextLocator : class, IDbContextLocator
    {
        protected bool IsTenantFilterEnabled => DataFilter.HasFilter(nameof(IHasTenant.TenantId)) ? DataFilter.IsFilterEnabled(nameof(IHasTenant.TenantId)) : false;

        public MultiTenantMasterContext(DbContextOptions<TDbContext> options) : base(options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            string connectionString = GetDatabaseConnectionString();
            if (!connectionString.IsNullOrWhiteSpace())
            {
                optionsBuilder.UseSqlServer(connectionString);
            }
            base.OnConfiguring(optionsBuilder);
        }

        public abstract string GetDatabaseConnectionString();

        protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
        {
            Expression<Func<TEntity, bool>> expression = base.CreateFilterExpression<TEntity>();
            if (typeof(IHasTenant).IsAssignableFrom(typeof(TEntity)))
            {
                Expression<Func<TEntity, bool>> tenantFilter = e => !IsTenantFilterEnabled || GetTenantId() == null || !(GetTenantId() is string) || (GetTenantId() as string) == string.Empty || ((IHasTenant)e).TenantId.Equals(GetTenantId().ToString());
                expression = expression == null ? tenantFilter : expression.And(tenantFilter);
            }
            return expression;
        }

        protected override void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> result)
        {
            // 获取当前事件对应上下文
            DbContext dbContext = eventData.Context;

            // 获取所有新增和更新的实体
            IEnumerable<EntityEntry> entries = dbContext.ChangeTracker.Entries()
                        .Where(u => u.State == EntityState.Added || u.State == EntityState.Modified);

            foreach (EntityEntry entry in entries)
            {
                object entityObj = entry.Entity;
                switch (entry.State)
                {
                    // 自动设置租户Id
                    case EntityState.Added:
                        object value = GetTenantId();
                        if (entityObj is IHasTenant && value != null && value is string)
                        {
                            IHasTenant entity = entityObj.As<IHasTenant>();
                            string tenantId = value.ToString();
                            if (entity.TenantId.IsNullOrWhiteSpace() && !tenantId.IsNullOrWhiteSpace())
                            {
                                entity.TenantId = tenantId;
                            }
                        }
                        break;
                    // 排除租户Id
                    case EntityState.Modified:
                        if (entityObj is IHasTenant)
                        {
                            entry.Property(nameof(IHasTenant.TenantId)).IsModified = false;
                        }
                        break;
                }
            }

            base.SavingChangesEvent(eventData, result);
        }

        public virtual object GetTenantId()
        {
           return YSession.GetTenantId();
        }
    }
}